{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "7134fad1",
      "metadata": {
        "id": "7134fad1"
      },
      "source": [
        "# 第二章 OpenAI 函数调用 OpenAI Function Calling\n",
        "\n",
        "本章主要介绍 OpenAI 的 API 提供的新功能，在最近几个月，OpenAI 对一些新的模型进行了微调，分别是`gpt-3.5-turbo-0613`和`gpt-4-0613`。\n",
        "\n",
        "假如我们有一个工具函数，在一些特定情况下需要调用，经过微调后，这些模型能够传入新的参数，可以通过这个新的参数来自动判断是否调用工具函数，如果判断需要调用工具函数，会返回这个工具函数和对应的输入参数。\n",
        "\n",
        "\n",
        "- [一、OpenAI函数新参数](#一openai函数新参数)\n",
        "  - [1.1 简单例子：得到当前天气](#11-简单例子得到当前天气)  \n",
        "  - [1.2 新参数：functions](#12-新参数functions) \n",
        "  - [1.3 相关提示调用结果](#13-相关提示调用结果)    \n",
        "  - [1.4 无关提示调用结果](#14-无关提示调用结果)\n",
        "- [二、Function Call参数模式](#二function-call参数模式)\n",
        "  - [2.1 自动判断是否调用](#21-自动判断是否调用)    \n",
        "  - [2.2 强制不调用](#22-强制不调用)    \n",
        "  - [2.3 强制调用](#23-强制调用)  \n",
        "- [三、函数调用以及执行函数](#三函数调用以及执行函数)   \n",
        "- [四、英文版提示](#四英文版提示)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d0d4e112",
      "metadata": {},
      "source": [
        "## 一、OpenAI函数新参数\n",
        "\n",
        "首先我们直接定义使用`OPENAI_API_KEY`，方便后续去调用OpenAI的API接口，并利用其函数。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "68b323d9",
      "metadata": {
        "id": "68b323d9"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "import openai\n",
        "\n",
        "os.environ['OPENAI_API_KEY'] = \"YOUR_API_KEY\"\n",
        "openai.api_key = os.environ['OPENAI_API_KEY']"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fba14f39",
      "metadata": {
        "id": "fba14f39"
      },
      "source": [
        "### 1.1 简单例子：得到当前天气\n",
        "\n",
        "首先我们使用 OpenAI 的使用的第一个例子，定义了一个`get_current_weather`的函数，正常来说，获取当前天气是语言模型本身不能完全做到的事情。因此，我们希望能够将语言模型和这样的函数结合起来，以当前的信息来增强它。\n",
        "\n",
        "在这个函数，我们固定了返回的值，比如温度固定为 22 摄氏度，但在实际应用中，这可能涉及到调用天气   API 或一些外部知识源。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "041aacb9",
      "metadata": {
        "id": "041aacb9"
      },
      "outputs": [],
      "source": [
        "import json\n",
        "\n",
        "# 定义一个函数，用于获取给定位置的当前天气\n",
        "def get_current_weather(location, unit=\"摄氏度\"):\n",
        "    \"\"\"获取指定位置的当前天气\"\"\"\n",
        "    # 模拟返回相同的天气情况的示例函数\n",
        "    # 在实际应用环境中，这可以是天气API\n",
        "    # 创建一个天气信息的字典\n",
        "    weather_info = {\n",
        "        \"location\": location,  # 天气的位置\n",
        "        \"temperature\": \"22\",  # 温度\n",
        "        \"unit\": unit,  # 温度单位，默认为摄氏度\n",
        "        \"forecast\": [\"晴\", \"多云\"],  # 天气预报\n",
        "    }\n",
        "    # 将天气信息转换为JSON格式的字符串并返回\n",
        "    return json.dumps(weather_info)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a7ba1a92",
      "metadata": {
        "id": "a7ba1a92"
      },
      "source": [
        "### 1.2 新参数：functions\n",
        "\n",
        "那我们如何将这样的函数传给语言模型呢？OpenAI引入了一个名为`functions`的新参数，通过该参数，我们可以传递一个函数定义列表。由于我们只有一个函数，所以列表中只有一个元素。这是一个JSON对象，具有几个不同的参数。\n",
        "\n",
        "- name：函数的名称\n",
        "- description：函数的描述\n",
        "- parameters：参数对象，里面有一些属性设置\n",
        "  - type：类型\n",
        "  - properties：本身是一个对象，传入的是对应的参数的描述。在例子中我们可以看到有两个元素，`location`和`unit`。每个这些元素都有一个类型，即字符串，然后是一个描述。`unit`是一个外部参数的设置，比如这里我们希望它可以是摄氏度或华氏度，所以我们可以在这里定义他的类型和以及枚举参数值。\n",
        "  - required：必填的参数，比如这里我们需要的参数就是`location`。\n",
        "\n",
        "在函数定义中，`description`以及在`properties`中的参数非常重要，因为这些将直接传递给语言模型，语言模型将使用这些描述来判断是否使用此函数。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "d00a4ad8",
      "metadata": {
        "id": "d00a4ad8"
      },
      "outputs": [],
      "source": [
        "# 定义一个函数\n",
        "functions = [\n",
        "    {\n",
        "        \"name\": \"get_current_weather\",\n",
        "        \"description\": \"获取指定位置的当前天气情况\",\n",
        "        \"parameters\": {\n",
        "            \"type\": \"object\",\n",
        "            \"properties\": {\n",
        "                \"location\": {\n",
        "                    \"type\": \"string\",\n",
        "                    \"description\": \"城市和省份，例如：北京，北京市\",\n",
        "                },\n",
        "                \"unit\": {\"type\": \"string\", \"enum\": [\"摄氏度\", \"华氏度\"]},\n",
        "            },\n",
        "            \"required\": [\"location\"],\n",
        "        },\n",
        "    }\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ecfb5fd6",
      "metadata": {
        "id": "ecfb5fd6"
      },
      "source": [
        "### 1.3 相关提示调用结果\n",
        "\n",
        "接下来我们就可以定义一个有关于天气的问题，如”北京的天气怎么样？“，然后使用 OpenAI 的函数进行调用对话的 API。首先我们得选取较新的模型，如`gpt-3.5-turbo-0613`，然后我们再将上述定义的        function 函数传入，查看最后的响应结果。\n",
        "\n",
        "从结果上来看，返回的消息的角色是助手，内容为空，而是有一个函数调用参数`function_call`，其中包含两个对象，`name`和`arguments`。`Name`是`get_current_weather`，这与我们传递的函数的名称相同，然后`arguments`是这个 JSON 格式的字典，里面是我们需要的参数。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6c9d6e3a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"finish_reason\": \"function_call\",\n",
            "      \"index\": 0,\n",
            "      \"logprobs\": null,\n",
            "      \"message\": {\n",
            "        \"content\": null,\n",
            "        \"function_call\": {\n",
            "          \"arguments\": \"{\\n  \\\"location\\\": \\\"\\u5317\\u4eac\\uff0c\\u5317\\u4eac\\u5e02\\\",\\n  \\\"unit\\\": \\\"\\u6444\\u6c0f\\u5ea6\\\"\\n}\",\n",
            "          \"name\": \"get_current_weather\"\n",
            "        },\n",
            "        \"role\": \"assistant\"\n",
            "      }\n",
            "    }\n",
            "  ],\n",
            "  \"created\": 1709602992,\n",
            "  \"id\": \"chatcmpl-8zE7kIlpxzjsiuxH4q6wtRh8CLDH3\",\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"system_fingerprint\": null,\n",
            "  \"usage\": {\n",
            "    \"completion_tokens\": 30,\n",
            "    \"prompt_tokens\": 95,\n",
            "    \"total_tokens\": 125\n",
            "  }\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "import openai\n",
        "\n",
        "# 定义输入消息\n",
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"北京的天气怎么样?\"\n",
        "    }\n",
        "]\n",
        "\n",
        "# 调用OpenAI的ChatCompletion API获取响应\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions # 传入function参数\n",
        ")\n",
        "\n",
        "# 打印响应结果\n",
        "print(response)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "388c7dd5",
      "metadata": {},
      "source": [
        "我们也可以看到响应的参数，比如在这里就是两个参数，分别是`location`和`unit`。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "88851464",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"location\": \"北京，北京市\",\n",
            "  \"unit\": \"摄氏度\"\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "# 打印传入参数\n",
        "print(response[\"choices\"][0][\"message\"][\"function_call\"][\"arguments\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c04f92a6",
      "metadata": {
        "id": "c04f92a6"
      },
      "source": [
        "如果我们仔细看响应的话，我们会发现内容现在是空的，`function_call`是一个字典，`function_call`中的`arguments`参数本身也是一个 JSON 字典。因此我们可以使用`json.loads`将其加载到 Python 字典中。它返回的参数可以直接传递给我们上述定义的`get_current_weather`函数。\n",
        "\n",
        "我们会发现，使用 OpenAI 进行函数调用并不直接调用工具函数，我们还是需要去调用工具函数，它只是告诉我们要调用哪个函数，即名称，以及该函数的参数应该是什么。以及由于他并没有去执行函数，所以说，如果我们在使用`json.loads`解码的时候遇到了一些问题，那实际上是模型的问题，所以这一部分可以考虑在写工具函数的时候做一些保护措施，后续也会讨论这一个点。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "23c71606",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"content\": null,\n",
            "  \"function_call\": {\n",
            "    \"arguments\": \"{\\n  \\\"location\\\": \\\"\\u5317\\u4eac\\uff0c\\u5317\\u4eac\\u5e02\\\",\\n  \\\"unit\\\": \\\"\\u6444\\u6c0f\\u5ea6\\\"\\n}\",\n",
            "    \"name\": \"get_current_weather\"\n",
            "  },\n",
            "  \"role\": \"assistant\"\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "# 获取response里面的message信息\n",
        "response_message = response[\"choices\"][0][\"message\"]\n",
        "\n",
        "print(response_message)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1f47fc65",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"location\": \"北京，北京市\",\n",
            "  \"unit\": \"摄氏度\"\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "# 打印参数\n",
        "print(response[\"choices\"][0][\"message\"][\"function_call\"][\"arguments\"])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "03007bf0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'{\"location\": {\"location\": \"\\\\u5317\\\\u4eac\\\\uff0c\\\\u5317\\\\u4eac\\\\u5e02\", \"unit\": \"\\\\u6444\\\\u6c0f\\\\u5ea6\"}, \"temperature\": \"22\", \"unit\": \"\\\\u6444\\\\u6c0f\\\\u5ea6\", \"forecast\": [\"\\\\u6674\", \"\\\\u591a\\\\u4e91\"]}'"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# 将JSON格式的字符串转换为Python对象\n",
        "args = json.loads(response_message[\"function_call\"][\"arguments\"])\n",
        "\n",
        "# 调用get_current_weather函数并传入参数args\n",
        "get_current_weather(args)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "31263afb",
      "metadata": {
        "id": "31263afb"
      },
      "source": [
        "### 1.4 无关提示调用结果\n",
        "\n",
        "接下来，我们也探讨一下，如果问的问题与函数无关会产生什么样的，也就是与天气无关，会返回什么样的信息呢？从结果上来看，我们可以返回的内容正常并且没有`function_call`参数，也就是在语言模型中判断不使用工具函数。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4a1ea679",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"finish_reason\": \"stop\",\n",
            "      \"index\": 0,\n",
            "      \"logprobs\": null,\n",
            "      \"message\": {\n",
            "        \"content\": \"\\u4f60\\u597d\\uff01\\u6709\\u4ec0\\u4e48\\u6211\\u53ef\\u4ee5\\u5e2e\\u52a9\\u4f60\\u7684\\u5417\\uff1f\",\n",
            "        \"role\": \"assistant\"\n",
            "      }\n",
            "    }\n",
            "  ],\n",
            "  \"created\": 1709602993,\n",
            "  \"id\": \"chatcmpl-8zE7lZ3keV5JGVTFqvAWOGxit4cXj\",\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"system_fingerprint\": null,\n",
            "  \"usage\": {\n",
            "    \"completion_tokens\": 19,\n",
            "    \"prompt_tokens\": 88,\n",
            "    \"total_tokens\": 107\n",
            "  }\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "# 与天气无关提示调用\n",
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"你好!\",\n",
        "    }\n",
        "]\n",
        "\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        "    function_call=\"auto\",\n",
        ")\n",
        "\n",
        "print(response)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6ed07bb6",
      "metadata": {},
      "source": [
        "由于编码问题，我们可以单独打印对应的返回的文本信息"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a89df974",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "你好！有什么我可以帮助你的吗？\n"
          ]
        }
      ],
      "source": [
        "print(response[\"choices\"][0][\"message\"][\"content\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c67fac41",
      "metadata": {
        "id": "c67fac41"
      },
      "source": [
        "## 二、Function Call参数模式\n",
        "\n",
        "`function_call`参数一共有3种模式，我们可以传递其他参数`function_call`以强制模型使用或不使用函数。\n",
        "\n",
        "1. 默认情况下，它设置为`auto`，也就是模型自行选择。\n",
        "2. 在第二种模式中，我们可以强制它调用一个函数。如果我们希望始终返回函数\n",
        "3. 另一种模式是`none`。这会强制语言模型不使用提供的任何函数。\n",
        "\n",
        "### 2.1 自动判断是否调用\n",
        "\n",
        "`auto`模式就是大模型自行选择是否要返回参数，这一部分也是默认的，上述所有的方式都是`auto`的模式\n",
        "\n",
        "### 2.2 强制不调用\n",
        "\n",
        "`none`模式对于这个例子来说并不重要，因为内容`你好`不需要函数调用，所以我们看到它没有被使用。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "ba400aa9",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ba400aa9",
        "outputId": "8b563e4f-c447-45a7-eb42-6390488a80b9"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"id\": \"chatcmpl-8nfuv5tlp6UaSwUfmeHppJkQyFpCW\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"created\": 1706849893,\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"index\": 0,\n",
            "      \"message\": {\n",
            "        \"role\": \"assistant\",\n",
            "        \"content\": \"\\u4f60\\u597d\\uff01\\u6709\\u4ec0\\u4e48\\u53ef\\u4ee5\\u5e2e\\u52a9\\u4f60\\u7684\\u5417\\uff1f\"\n",
            "      },\n",
            "      \"logprobs\": null,\n",
            "      \"finish_reason\": \"stop\"\n",
            "    }\n",
            "  ],\n",
            "  \"usage\": {\n",
            "    \"prompt_tokens\": 88,\n",
            "    \"completion_tokens\": 17,\n",
            "    \"total_tokens\": 105\n",
            "  },\n",
            "  \"system_fingerprint\": null\n",
            "}\n",
            "你好！有什么可以帮助你的吗？\n"
          ]
        }
      ],
      "source": [
        "# 无关提示强制不调用\n",
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"你好\",\n",
        "    }\n",
        "]\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        "    function_call=\"none\", # 传入参数强制不调用\n",
        ")\n",
        "print(response)\n",
        "print(response[\"choices\"][0][\"message\"][\"content\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3bcd0bb1",
      "metadata": {
        "id": "3bcd0bb1"
      },
      "source": [
        "那如果我们在需要使用工具的函数时候强制不调用（也就是使用第二种模式）会出现什么结果呢？从结果上来看，他依然有正常的`role`和`content`，但是因为我们强制不调用函数，所以他在试图返回正确的参数，但是并不正确。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "d19386d5",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "d19386d5",
        "outputId": "0f206c8e-21e9-4aca-c6b2-7674650d723e"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"id\": \"chatcmpl-8nfv0wFX6tqcaoU2gT4KqICymXDxa\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"created\": 1706849898,\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"index\": 0,\n",
            "      \"message\": {\n",
            "        \"role\": \"assistant\",\n",
            "        \"content\": \"\\u8bf7\\u7a0d\\u7b49\\uff0c\\u6211\\u4e3a\\u60a8\\u67e5\\u627e\\u5317\\u4eac\\u7684\\u5929\\u6c14\\u60c5\\u51b5\\u3002\"\n",
            "      },\n",
            "      \"logprobs\": null,\n",
            "      \"finish_reason\": \"stop\"\n",
            "    }\n",
            "  ],\n",
            "  \"usage\": {\n",
            "    \"prompt_tokens\": 95,\n",
            "    \"completion_tokens\": 18,\n",
            "    \"total_tokens\": 113\n",
            "  },\n",
            "  \"system_fingerprint\": null\n",
            "}\n",
            "请稍等，我为您查找北京的天气情况。\n"
          ]
        }
      ],
      "source": [
        "# 相关提示强制不调用\n",
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"北京天气怎么样?\",\n",
        "    }\n",
        "]\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        "    function_call=\"none\", # 传入参数强制不调用\n",
        ")\n",
        "print(response)\n",
        "print(response[\"choices\"][0][\"message\"][\"content\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4ed657cb",
      "metadata": {
        "id": "4ed657cb"
      },
      "source": [
        "### 2.3 强制调用\n",
        "\n",
        "`function_call`参数的最后一个模式是强制调用函数，方法也是很简单，只要在参数里面传入这个函数的名字即可。在这一部分，我们指定`name`为`get_current_weather`，这将强制它使用`get_current_weather`函数。如果我们查看结果，我们实际上会看到返回了这个`function_call`对象，其中有`name`为`get_current_weather`的一些参数。\n",
        "\n",
        "为了好玩，这里面传了一个于天气无关的参数，然后强制他使用`get_current_weather`函数，但我们传入的参数中绝对没有任何信息关于它应该如何调用该函数。所以在这里，他编造了北京，北京市的参数，如果我们再次运行它，它会不断地调用北京市的参数。\n",
        "\n",
        "我们还可以尝试使用不同的模型函数来调用，不同的参数值，不同的输入消息，但是这里面要注意一个事情，首先，函数本身和描述都会计入传递给OpenAI的令牌使用限制。所以如果我们运行这个，我们可以看到返回的提示令牌是95。如果我们注释掉`functions`和`function_call`，我们可以看到提示令牌减少到15。因为OpenAI模型对令牌有限制，因此，在构造要传递给OpenAI的消息时，现在不仅需要注意消息的长度，还需要注意传递的函数的长度。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "c432ee4c",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "c432ee4c",
        "outputId": "507ca457-04ae-4a97-f7cd-6c7efd278ea8"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"id\": \"chatcmpl-8nfvkQDayf7kmYNdx4G84dkl1L5YC\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"created\": 1706849944,\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"index\": 0,\n",
            "      \"message\": {\n",
            "        \"role\": \"assistant\",\n",
            "        \"content\": null,\n",
            "        \"function_call\": {\n",
            "          \"name\": \"get_current_weather\",\n",
            "          \"arguments\": \"{\\n  \\\"location\\\": \\\"\\u5317\\u4eac\\uff0c\\u5317\\u4eac\\u5e02\\\"\\n}\"\n",
            "        }\n",
            "      },\n",
            "      \"logprobs\": null,\n",
            "      \"finish_reason\": \"stop\"\n",
            "    }\n",
            "  ],\n",
            "  \"usage\": {\n",
            "    \"prompt_tokens\": 95,\n",
            "    \"completion_tokens\": 12,\n",
            "    \"total_tokens\": 107\n",
            "  },\n",
            "  \"system_fingerprint\": null\n",
            "}\n",
            "{\n",
            "  \"location\": \"北京，北京市\"\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "# 无关提示强制调用函数\n",
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"你好!\",\n",
        "    }\n",
        "]\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        "    function_call={\"name\": \"get_current_weather\"}, # 强制调用函数get_current_weather\n",
        ")\n",
        "print(response)\n",
        "print(response[\"choices\"][0][\"message\"][\"function_call\"][\"arguments\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f3306ca8",
      "metadata": {
        "id": "f3306ca8"
      },
      "source": [
        "## 三、函数调用以及执行函数\n",
        "\n",
        "最后，让我们看一下如何将这些函数调用和实际执行函数调用的结果传递回语言模型。这很重要，因为通常我们希望使用语言模型确定要调用的函数，然后运行该函数，但然后将其传递回语言模型以获得最终响应。\n",
        "\n",
        "我们会经过这样的流程，首先我们问问题得到一个带有`function call`参数的响应，然后我们将这个消息添加到我们的消息列表中。然后我们可以模拟使用语言模型提供的参数调用`get_current_weather`函数，并且保存到一个变量`observation`中。接着我们定义一个新的消息列表，表示刚刚调用函数的结果，这里面有一个重要的点就是`role`等于`function`，也就是告诉语言模型这是调用函数的响应。除此之外，还传递函数的名称`name`以及`content`变量，设为上述计算的`observation`。\n",
        "\n",
        "如果我们然后使用这个消息列表调用语言模型，我们可以看到语言模型回答的非常好：北京的天气目前是22摄氏度，天气以晴天为主，也有多云的情况。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f1636ae0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"finish_reason\": \"stop\",\n",
            "      \"index\": 0,\n",
            "      \"logprobs\": null,\n",
            "      \"message\": {\n",
            "        \"content\": \"\\u4f60\\u597d\\uff01\\u6709\\u4ec0\\u4e48\\u6211\\u53ef\\u4ee5\\u5e2e\\u52a9\\u4f60\\u7684\\u5417\\uff1f\",\n",
            "        \"role\": \"assistant\"\n",
            "      }\n",
            "    }\n",
            "  ],\n",
            "  \"created\": 1709602993,\n",
            "  \"id\": \"chatcmpl-8zE7lZ3keV5JGVTFqvAWOGxit4cXj\",\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"system_fingerprint\": null,\n",
            "  \"usage\": {\n",
            "    \"completion_tokens\": 19,\n",
            "    \"prompt_tokens\": 88,\n",
            "    \"total_tokens\": 107\n",
            "  }\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "# 与天气无关提示调用\n",
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"你好!\",\n",
        "    }\n",
        "]\n",
        "\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        "    function_call=\"auto\",\n",
        ")\n",
        "\n",
        "print(response)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b1577b5b",
      "metadata": {},
      "source": [
        "由于编码问题，我们可以单独答应对应的返回的文本信息"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2f6dff4a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "你好！有什么我可以帮助你的吗？\n"
          ]
        }
      ],
      "source": [
        "print(response[\"choices\"][0][\"message\"][\"content\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5940bf6d",
      "metadata": {},
      "source": [
        "## 四、英文版提示\n",
        "\n",
        "**1.1 简单例子：得到当前天气**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "61a23996",
      "metadata": {
        "id": "61a23996"
      },
      "outputs": [],
      "source": [
        "import json\n",
        "\n",
        "# Example dummy function hard coded to return the same weather\n",
        "# In production, this could be your backend API or an external API\n",
        "def get_current_weather(location, unit=\"fahrenheit\"):\n",
        "    \"\"\"Get the current weather in a given location\"\"\"\n",
        "    weather_info = {\n",
        "        \"location\": location,\n",
        "        \"temperature\": \"72\",\n",
        "        \"unit\": unit,\n",
        "        \"forecast\": [\"sunny\", \"windy\"],\n",
        "    }\n",
        "    return json.dumps(weather_info)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "44c5d2d2",
      "metadata": {
        "id": "44c5d2d2"
      },
      "source": [
        "**1.2 定义function函数**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "4152b105",
      "metadata": {
        "id": "4152b105"
      },
      "outputs": [],
      "source": [
        "# define a function\n",
        "functions = [\n",
        "    {\n",
        "        \"name\": \"get_current_weather\",\n",
        "        \"description\": \"Get the current weather in a given location\",\n",
        "        \"parameters\": {\n",
        "            \"type\": \"object\",\n",
        "            \"properties\": {\n",
        "                \"location\": {\n",
        "                    \"type\": \"string\",\n",
        "                    \"description\": \"The city and state, e.g. San Francisco, CA\",\n",
        "                },\n",
        "                \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n",
        "            },\n",
        "            \"required\": [\"location\"],\n",
        "        },\n",
        "    }\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "668eb81c",
      "metadata": {
        "id": "668eb81c"
      },
      "source": [
        "**1.3 相关提示调用结果**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "a6bd2994",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "a6bd2994",
        "outputId": "ae7da376-c365-4177-a1d0-a4df92f6815e"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"id\": \"chatcmpl-8nfyFSmPyo4RoV79D5Urk1QzLCf1C\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"created\": 1706850099,\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"index\": 0,\n",
            "      \"message\": {\n",
            "        \"role\": \"assistant\",\n",
            "        \"content\": null,\n",
            "        \"function_call\": {\n",
            "          \"name\": \"get_current_weather\",\n",
            "          \"arguments\": \"{\\n  \\\"location\\\": \\\"Boston, MA\\\"\\n}\"\n",
            "        }\n",
            "      },\n",
            "      \"logprobs\": null,\n",
            "      \"finish_reason\": \"function_call\"\n",
            "    }\n",
            "  ],\n",
            "  \"usage\": {\n",
            "    \"prompt_tokens\": 82,\n",
            "    \"completion_tokens\": 18,\n",
            "    \"total_tokens\": 100\n",
            "  },\n",
            "  \"system_fingerprint\": null\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"What's the weather like in Boston?\"\n",
        "    }\n",
        "]\n",
        "\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions\n",
        ")\n",
        "\n",
        "print(response)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a60020d4",
      "metadata": {
        "id": "a60020d4"
      },
      "source": [
        "**1.4 无关提示调用结果**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "b04b3318",
      "metadata": {
        "id": "b04b3318"
      },
      "outputs": [],
      "source": [
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"hi!\",\n",
        "    }\n",
        "]\n",
        "\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bfdbfe18",
      "metadata": {
        "id": "bfdbfe18"
      },
      "source": [
        "**2.1 自动判断是否调用**\n",
        "\n",
        "自动判断是否调用函数，判断提示是否和函数相关"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "e8c48d2b",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "e8c48d2b",
        "outputId": "cf492711-60a5-42a7-e154-09a3b3174da1"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"id\": \"chatcmpl-8nfyHfC32MVE2gpE6HZxtmC7f7Mvi\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"created\": 1706850101,\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"index\": 0,\n",
            "      \"message\": {\n",
            "        \"role\": \"assistant\",\n",
            "        \"content\": \"Hello! How can I assist you today?\"\n",
            "      },\n",
            "      \"logprobs\": null,\n",
            "      \"finish_reason\": \"stop\"\n",
            "    }\n",
            "  ],\n",
            "  \"usage\": {\n",
            "    \"prompt_tokens\": 76,\n",
            "    \"completion_tokens\": 10,\n",
            "    \"total_tokens\": 86\n",
            "  },\n",
            "  \"system_fingerprint\": null\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"hi!\",\n",
        "    }\n",
        "]\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        "    function_call=\"auto\",\n",
        ")\n",
        "print(response)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "32eb9b08",
      "metadata": {
        "id": "32eb9b08"
      },
      "source": [
        "**2.2 强制不调用**\n",
        "\n",
        "无关提示强制不调用，结果正常"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "ba53560e",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ba53560e",
        "outputId": "c529ab29-8c49-4b2c-9645-10ae8c018f7b"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"id\": \"chatcmpl-8nfyfrOoacEQCPO3O2sYLukAdbxX4\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"created\": 1706850125,\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"index\": 0,\n",
            "      \"message\": {\n",
            "        \"role\": \"assistant\",\n",
            "        \"content\": \"Hello! How can I assist you today?\"\n",
            "      },\n",
            "      \"logprobs\": null,\n",
            "      \"finish_reason\": \"stop\"\n",
            "    }\n",
            "  ],\n",
            "  \"usage\": {\n",
            "    \"prompt_tokens\": 77,\n",
            "    \"completion_tokens\": 9,\n",
            "    \"total_tokens\": 86\n",
            "  },\n",
            "  \"system_fingerprint\": null\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"hi!\",\n",
        "    }\n",
        "]\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        "    function_call=\"none\",\n",
        ")\n",
        "print(response)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e037da0",
      "metadata": {
        "id": "0e037da0"
      },
      "source": [
        "**2.3 强制调用**\n",
        "\n",
        "强制无关问题的调用"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "32b16792",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "32b16792",
        "outputId": "37b06217-4632-46e4-ce07-453e73814081"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"id\": \"chatcmpl-8nfyzFFeUyzJDYVbdiVDgPEtNrnKE\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"created\": 1706850145,\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"index\": 0,\n",
            "      \"message\": {\n",
            "        \"role\": \"assistant\",\n",
            "        \"content\": null,\n",
            "        \"function_call\": {\n",
            "          \"name\": \"get_current_weather\",\n",
            "          \"arguments\": \"{\\n  \\\"location\\\": \\\"San Francisco, CA\\\"\\n}\"\n",
            "        }\n",
            "      },\n",
            "      \"logprobs\": null,\n",
            "      \"finish_reason\": \"stop\"\n",
            "    }\n",
            "  ],\n",
            "  \"usage\": {\n",
            "    \"prompt_tokens\": 83,\n",
            "    \"completion_tokens\": 12,\n",
            "    \"total_tokens\": 95\n",
            "  },\n",
            "  \"system_fingerprint\": null\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "\n",
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"hi!\",\n",
        "    }\n",
        "]\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        "    function_call={\"name\": \"get_current_weather\"},\n",
        ")\n",
        "print(response)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "65b85a74",
      "metadata": {
        "id": "65b85a74"
      },
      "source": [
        "强制有关问题调用，结果和`auto`一样"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "b6f955de",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "b6f955de",
        "outputId": "b8134f2e-1746-4139-83f4-ac51327549cb"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  \"id\": \"chatcmpl-8nfzav6WbYIDLPHXYwaBXTM0byxaj\",\n",
            "  \"object\": \"chat.completion\",\n",
            "  \"created\": 1706850182,\n",
            "  \"model\": \"gpt-3.5-turbo-0613\",\n",
            "  \"choices\": [\n",
            "    {\n",
            "      \"index\": 0,\n",
            "      \"message\": {\n",
            "        \"role\": \"assistant\",\n",
            "        \"content\": null,\n",
            "        \"function_call\": {\n",
            "          \"name\": \"get_current_weather\",\n",
            "          \"arguments\": \"{\\n  \\\"location\\\": \\\"Boston, MA\\\"\\n}\"\n",
            "        }\n",
            "      },\n",
            "      \"logprobs\": null,\n",
            "      \"finish_reason\": \"stop\"\n",
            "    }\n",
            "  ],\n",
            "  \"usage\": {\n",
            "    \"prompt_tokens\": 89,\n",
            "    \"completion_tokens\": 11,\n",
            "    \"total_tokens\": 100\n",
            "  },\n",
            "  \"system_fingerprint\": null\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "messages = [\n",
        "    {\n",
        "        \"role\": \"user\",\n",
        "        \"content\": \"What's the weather like in Boston!\",\n",
        "    }\n",
        "]\n",
        "response = openai.ChatCompletion.create(\n",
        "    model=\"gpt-3.5-turbo-0613\",\n",
        "    messages=messages,\n",
        "    functions=functions,\n",
        "    function_call={\"name\": \"get_current_weather\"},\n",
        ")\n",
        "print(response)"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.9.13"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
